Spring Cloud Hoxton 升级到 Jubilee
January 12, 2022
在维护那篇文章中,我们把Spring Cloud从Finchley到Hoxton,这里记录下从Hoxton升级到Jubilee(2021.0.0)的过程。
首先,根据Jubilee要求至少是Spring boot 2.6.1+,而之前的Hoxton只要求2.3.x,所以升级的时候,要指定正确的boot版本
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.2</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-cloud.version>2021.0.0</spring-cloud.version>
<java.version>1.8</java.version>
</properties>
然后你就会发现编译不通过,有如下错误:
这是由于在新版本2020.0.x(Ilford)中,引入了如下Break Changes:
-
Spring Cloud Security相关代码被迁移到Spring Cloud Common中。但是对于没有迁移的,仍然需要单独指定。
-
Spring-cloud-netflix做了大幅精简,以下模块全被去掉,仍需要,则要单独指定。
spring-cloud-netflix-archaius
spring-cloud-netflix-concurrency-limits
spring-cloud-netflix-core
spring-cloud-netflix-dependencies
spring-cloud-netflix-hystrix
spring-cloud-netflix-hystrix-contract
spring-cloud-netflix-hystrix-dashboard
spring-cloud-netflix-hystrix-stream
spring-cloud-netflix-ribbon
spring-cloud-netflix-sidecar
spring-cloud-netflix-turbine
spring-cloud-netflix-turbine-stream
spring-cloud-netflix-zuul
spring-cloud-starter-netflix-archaius
spring-cloud-starter-netflix-hystrix
spring-cloud-starter-netflix-hystrix-dashboard
spring-cloud-starter-netflix-ribbon
spring-cloud-starter-netflix-turbine
spring-cloud-starter-netflix-turbine-stream
spring-cloud-starter-netflix-zuul
Support for ribbon, hystrix and zuul was removed across the release train projects.
具体来说就是在最顶级的pom文件中的dependency management加入下面的依赖就可以了。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-hystrix-stream</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine-stream</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
还有另外一个小小的改变,新版本的Spring test套件不再包含junit,所以也需要额外指定,直接在最顶级pom里面加上即可。
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
接下来运行的时候,你会遇到这个错误
org.springframework.cloud.commons.ConfigDataMissingEnvironmentPostProcessor$ImportException: No spring.config.import set
at org.springframework.cloud.commons.ConfigDataMissingEnvironmentPostProcessor.postProcessEnvironment(ConfigDataMissingEnvironmentPostProcessor.java:80)
at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEnvironmentPreparedEvent(EnvironmentPostProcessorApplicationListener.java:102)
at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEvent(EnvironmentPostProcessorApplicationListener.java:87)
这个的原因是由于新版本引入的另外一个Break Change造成的。
- Spring Cloud Commons的Bootstrap被默认禁用。导入配置的新方法采用Spring Boot 2.4新出的spring.config.import功能。同时spring.config.import加入了对解密的支持。对于Config Client、Consul、Vault和Zookeeper的配置导入细节可参阅相应文档。如果你需要使用原来的配置引导功能,那么需要将org.springframework.cloud:spring-cloud-starter-bootstrap依赖引入到工程中。
所以需要在最顶级的pom中加入依赖即可
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
</dependencies>
- Spring Security 问题,虽然上面通过引入spring security oauth2解决了编译问题,但是,运行时会遇到下面的问题
Description:
Field accessTokenContextRelay in org.springframework.cloud.commons.security.ResourceServerTokenRelayAutoConfiguration$ResourceServerTokenRelayRegistrationAutoConfiguration required a bean of type 'org.springframework.cloud.commons.security.AccessTokenContextRelay' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'org.springframework.cloud.commons.security.AccessTokenContextRelay' in your configuration.
这个问题,有人在github上向Spring报告过,但是双方都不知道对方在说什么。其实问题根源很简单,就是由于部分Security代码已经迁移到Spring Cloud Common中去,而系统中,现在有两套重复的代码,当然就不行了,解决办法就是排除掉老的。如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-security</artifactId>
</exclusion>
</exclusions>
</dependency>
另外,org.springframework.cloud.security.oauth2.client.feign.OAuth2FeignRequestInterceptor
也已经更名为org.springframework.cloud.openfeign.security.OAuth2FeignRequestInterceptor
结果,我又遇到另外一个问题。
Description:
The dependencies of some of the beans in the application context form a cycle:
┌──->──┐
| org.springframework.cloud.netflix.hystrix.stream.HystrixStreamAutoConfiguration (field private org.springframework.cloud.netflix.hystrix.stream.HystrixStreamProperties org.springframework.cloud.netflix.hystrix.stream.HystrixStreamAutoConfiguration.properties)
└──<-──┘
Action:
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
这个循环依赖的问题是由于从Spring boot 2.6.0开始默认禁止循环依赖了。所以需要手动允许。
spring:
main:
allow-circular-references: true
然后就可以正常启动了。
期待不要有问题了,结果还是有,啥也别说,这就是人生。
java.lang.NoSuchMethodError: org.springframework.boot.web.servlet.error.ErrorController.getErrorPath()Ljava/lang/String;
at org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping.lookupHandler(ZuulHandlerMapping.java:87) ~[spring-cloud-netflix-zuul-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.web.servlet.handler.AbstractUrlHandlerMapping.getHandlerInternal(AbstractUrlHandlerMapping.java:152) ~[spring-webmvc-5.3.14.jar:5.3.14]
at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:498) ~[spring-webmvc-5.3.14.jar:5.3.14]
at org.springframework.web.servlet.DispatcherServlet.getHandler(DispatcherServlet.java:1261) ~[spring-webmvc-5.3.14.jar:5.3.14]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1043) ~[spring-webmvc-5.3.14.jar:5.3.14]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) ~[spring-webmvc-5.3.14.jar:5.3.14]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.14.jar:5.3.14]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.14.jar:5.3.14]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:655) ~[tomcat-embed-core-9.0.56.jar:4.0.FR]
还真是,Spring Cloud 已经不带Ribbon,Hystrix,Zuul玩了,还好有人自己动手利用PostProcessor解救了自己。这样这个问题也算解决了。
问题继续,这次不是ZUUL了,是Ribbon,果然好兄弟就是要一起出现。
com.netflix.zuul.exception.ZuulException: Forwarding error
at org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.handleException(RibbonRoutingFilter.java:198) ~[spring-cloud-netflix-zuul-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.forward(RibbonRoutingFilter.java:173) ~[spring-cloud-netflix-zuul-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.run(RibbonRoutingFilter.java:119) ~[spring-cloud-netflix-zuul-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at com.netflix.zuul.ZuulFilter.runFilter(ZuulFilter.java:117) ~[zuul-core-1.3.1.jar:1.3.1]
......
Caused by: com.netflix.client.ClientException: Load balancer does not have available server for client: account-service
at com.netflix.loadbalancer.LoadBalancerContext.getServerFromLoadBalancer(LoadBalancerContext.java:483) ~[ribbon-loadbalancer-2.3.0.jar:2.3.0]
at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:184) ~[ribbon-loadbalancer-2.3.0.jar:2.3.0]
这个问题很多人认为是要在Zuul中加上下面的配置才行:
ribbon:
eureka:
enable: true
结果并不行。经过半天的各种尝试,突然顿悟,问题已经很清楚了,新版本的Spring Cloud已经放弃Ribbon,即使有库,在运行时也没有load balancer了。我曾经尝试直接加上spring-cloud-start-loadbalancer
的依赖,结果也不行。路走到这里,说明项目要大改了,要用Spring Gateway代替Zuul,用Spring LB代替Ribbon。下回分解。